package org.codeart.ioc.reader;

import cn.hutool.core.util.StrUtil;
import org.codeart.ioc.annotation.Bean;
import org.codeart.ioc.annotation.Component;
import org.codeart.ioc.config.BeanDefinition;
import org.codeart.ioc.exception.BeanException;
import org.codeart.ioc.support.BeanDefinitionRegistry;
import org.codeart.ioc.support.SimpleBeanDefinitionRegistry;
import org.codeart.ioc.util.AnnotationUtil;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;

/**
 * 注解方式 BeanDefinition 读取器
 */
public class AnnotationDefinitionReader implements BeanDefinitionReader {
    
    private final Map<String, Object> singletonObjects;
    
    private final BeanDefinitionRegistry registry;

    public AnnotationDefinitionReader(Map<String, Object> singletonObjects) {
        this.singletonObjects = singletonObjects;
        this.registry = new SimpleBeanDefinitionRegistry();
    }

    public AnnotationDefinitionReader(Map<String, Object> singletonObjects, BeanDefinitionRegistry registry) {
        this.singletonObjects = singletonObjects;
        this.registry = registry;
    }

    @Override
    public BeanDefinitionRegistry getRegistry() {
        return registry;
    }

    @Override
    public void loadBeanDefinitions(String configLocation) throws Exception {
        // 极简的 IOC 容器，目前不考虑类似于 Spring 的注解的嵌套修饰，如 @Component 修饰 @Configuration
        List<Class<?>> classes = AnnotationUtil.scanComponents(configLocation, Component.class);
        // 所有被 @Bean 修饰的方法必须要是 public
        for (Class<?> clazz : classes) {
            // 解析 @Component
            Component component = clazz.getAnnotation(Component.class);
            String componentName = component.value();
            if (StrUtil.isEmpty(componentName)) {
                componentName = StrUtil.lowerFirst(clazz.getSimpleName());
            }
            BeanDefinition beanDefinition = new BeanDefinition(componentName, clazz.getName());
            registry.registerBeanDefinition(componentName, beanDefinition);
            
            // 解析 @Bean
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                // @Bean 注解存在，暂时不支持方法参数注入以及
                if (method.isAnnotationPresent(Bean.class)) {
                    Bean annotation = method.getAnnotation(Bean.class);
                    String beanName = annotation.value();
                    if (StrUtil.isEmpty(beanName)) {
                        beanName = method.getName();
                    }
                    Class<?> returnedType = method.getReturnType();
                    BeanDefinition definition = new BeanDefinition(beanName, returnedType.getName());
                    registry.registerBeanDefinition(beanName, definition);
                    Object obj = clazz.newInstance();
                    Object returnedObj = method.invoke(obj);
                    if (singletonObjects.containsKey(beanName)) {
                        throw new BeanException(String.format("Bean already exist: %s.\n", beanName));
                    }
                    singletonObjects.put(beanName, returnedObj);
                }
            }
        }
    }
}
